package com.desaysv.mvi

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.desaysv.mvi.intent.IIntent
import com.desaysv.mvi.model.BaseViewModel
import com.desaysv.mvi.state.IState
import kotlinx.coroutines.*
import java.lang.reflect.ParameterizedType

/**
 * @author linzi
 * @date 2023-3-31
 */
abstract class MVIActivity< T : BaseViewModel<E, K>, K : IState,E : IIntent> : AppCompatActivity() {
    private val TAG = "MVIActivity"
    lateinit var viewModel: T

    /**
     * state消费
     */
    abstract fun consumerStates(state:K)

    override fun onCreate(savedInstanceState: Bundle?) {
        initModel()
        super.onCreate(savedInstanceState)
        initConsumer()
    }

    /**
     * 调用Model发送intent
     */
    fun send(intent:E){
        doLaunch {
            viewModel.currentIntent.send(intent)
        }
    }

    /**
     * 初始化state的消费者
     */
    private fun initConsumer(){
        doLaunch {
            viewModel.currentState.collect{
                doMain {
                    consumerStates(it)
                }
            }
        }
    }

    /**
     * 直接从泛型类里面读取Model进行初始化
     */
    private fun initModel() {
        //识别泛型类
        val tc = (this.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments
        var claz: Class<T>? =null
        for (type in tc) {
            val clzTemp= type as Class<T>
            if (clzTemp.superclass === BaseViewModel::class.java) {
                claz = clzTemp
                break
            }
        }
        if (claz == null) {
            throw RuntimeException("need BaseViewModel impl")
        }
        viewModel = viewModels(claz)
    }

    /**
     * 初始化model
     */
    private fun <T : ViewModel> viewModels(cla: Class<T>): T {
        val provider =
            (ViewModelProvider.AndroidViewModelFactory(this.application) as ViewModelProvider.Factory?)?.let {
                ViewModelProvider(
                    this,
                    it
                )
            }
        return provider!![cla]
    }

    private fun doLaunch(block: suspend CoroutineScope.() -> Unit) {
        viewModel.viewModelScope.launch {
            block.invoke(this)
        }
    }
    private fun doMain(block: suspend CoroutineScope.() -> Unit) {
        viewModel.viewModelScope.launch (Dispatchers.Main){
            block.invoke(this)
        }
    }
}